'''
Created on 29/05/2013

@author: mirta
'''
from flask import Blueprint
from flask import render_template
from flask import flash
from flask import redirect
from flask import url_for

from app import db

from app.security import g

from app.models.item import Item
from app.models.fase import Fase
from app.models.permiso import Permiso


mod = Blueprint('calculo', __name__ , url_prefix='/calculo')

@mod.route('/individual/<item>/', methods=['GET', 'POST'])
def calculo_impacto_ind(item):
    """
    Metodo que calcula el impacto que tendria cambiar o eliminar un item sobre el proyecto
    @param item: id del item que se desea impactar
    @return: los detalles del item a impactar, los items afectados e informacion sobre el costo
    del impacto
    """
    
    # Verificamos que el usuario tenga asociado un proyecto para que pueda ver la pagina
    if g.user == None:
        flash('No tiene permiso para ver esta pagina.')
        return redirect(url_for('acceso.home'))
    
    # Obtenemos el item seleccionado por el usuario
    item_seleccionado = Item.query.filter_by(id=item).first()
    # Obtenemos la fase a la cual corresponde dicho item
    fase = item_seleccionado.fase
    
    # Verificamos que el usuario tenga permisos para ver detalles de la fase
    if(verificar_autorizacion_fase(usuario=g.user, fase=fase, permisoRequerido='VER')):
    
        # Si tiene permisos entonces se realiza la accion seleccionada
        
        # Primero obtenemos los items que seran afectados hacia adelante      
        lista_adelante = calculo_adelante(item_seleccionado, [])
        # Luego obtenemos los items que seran afectados hacia atras
        lista_atras = calculo_atras(item_seleccionado, [])
        # Calculamos el costo del impacto hacia adelante
        costo_adelante = calculo_costo(lista_adelante)
        # Calculamos el costo del impacto hacia atras
        costo_atras = calculo_costo(lista_atras)
    
        # Calculamos el costo total del impacto    
        costo_impacto = costo_adelante + costo_atras + item_seleccionado.costo
        #flash(costo_impacto)
    
        # Ademas del costo total del impacto del items seleccionado, obtenemos otros datos
        # como informaciones generales que seran mostrados en la vista al usuario
        
        # Obtenemos todoas las fases del proyecti
        lista_fases = Fase.query.filter_by(proyecto_id=g.proyecto.id)
        lista_fases = lista_fases.all()
        
        lista_items=[]
        
        # Por cada fase del proyecto
        for fase in lista_fases:
            
            # Obtenemos los items de la fase
            items = Item.query.filter_by(fase_id=fase.id)
            items = items.all()
            
            # Por cada item de la fase
            for item in items:
                # Agregamos a la lista de items del proyecto
                lista_items.append(item)
        
        # Calculamos el costo total del proyecto
        costo_items_total = calculo_costo(lista_items)
        
        # Calculamos el porcentaje del costo total que afecta el impacto del item
        porcentaje = float(costo_impacto)/float(costo_items_total)*100
        #flash(porcentaje)
        
        # Mostramos todas estas informaciones al usuario
        return render_template('calculo_impacto/impacto_individual.html', item=item_seleccionado,
                                lista_adelante=lista_adelante, lista_atras=lista_atras,
                                 porcentaje=porcentaje, costo_impacto=costo_impacto)
    else:
        # Si no se tiene permisos para realizar la accion lo indicamos con un mensaje
        # y redirigimos al usuario a la pagina de administracion de items
        flash('No tiene permisos para ver esta informacion') 
        return redirect(url_for('items.administracion_item'))
        

mod.route('/grupo/<items>/', methods=['GET', 'POST'])
def calculo_impacto_grup(items):
    """
    Metodo que calcula el impacto que tendra eliminar o cambiar un grupo de items en el proyecto
    @param items: lista de items que se quieren impactar
    @return: costo de impacto de la lista de items sobre el proyecto
    """
    #solicitud_seleccionada = SolicitudCambio.query.filter_by(id=solicitud).first()
    lista_adelante = []
    lista_atras = []
    lista_items = []
    
    # Por cada item en la lista
    for item in items:
        # Obtnemos la lista de items que seran afectados hacia adelante si se impacta ese item
        aux_adelante = calculo_adelante(item, [])
        # Por cada uno de los items afectados
        for aux in aux_adelante:
            # Si ya no se encuentra en la lista general de items afectados hacia adelante
            if not(aux in lista_adelante):
                # Se agrega el item a la lista
                lista_adelante.append(aux)
        
        # Obtenemos la lista de items que seran afectados hacia atras si se impacta ese item
        aux_atras = calculo_atras(item, [])
        # Por cada uno de los items afectados
        for aux in aux_atras:
             # Si ya no se encuentra en la lista general de items afectados hacia atras
            if not(aux in lista_atras):
                # Se agrega el item a la lista
                lista_atras.append(aux)
    
    # Por cada uno de los items de la lista recibida
    for item in items:
        # Si el item no se encuentra ya en la lista de items afectados hacia adelante y hacia atras
        if (not(item in lista_adelante)) and (not(item in lista_atras)):
            # Se agrega a una lista global de items afectados
            lista_items.append(item)
    
    # Por cada item en los lista de afectados hacia adelante
    for item in lista_adelante:
        # Si no se encuentra en la lista global de items afectados
        if (not(item in lista_items)):
            # Se agrega a la lista
            lista_items.append(item)
    
    # Por cada item en los lista de afectados hacia atras 
    for item in lista_atras:
         # Si no se encuentra en la lista global de items afectados
        if (not(item in lista_items)):
             # Se agrega a la lista
            lista_items.append(item)
    
    # Calculamos el costo hacia adelante
    #costo_adelante = calculo_costo(lista_adelante)
    #flash(costo_adelante)
    # Calculamos el costo hacia atras
    #costo_atras = calculo_costo(lista_atras)
    #flash(costo_atras)
    
    # Calculamos el costo del impacto de los items afectados
    costo_impacto = calculo_costo(lista_items)
    
    # Retornamos el costo del impacto de la lista de items
    return costo_impacto

    
def calculo_adelante(item, lista_items):
    """
    Metodo que obtiene todos los items que seran afectado hacia adelante por el impacto de un item
    @param item: item que sera analizado hacia adelante
    @param lista_items: lista de los items afectados, la cual sera actualizada
    @return: lista de items afectadoa hacia adelante
    """
    
    # Obtenemos los hijos del item recibido
    hijos = Item.query.filter_by(padre_id=item.id)
    hijos = hijos.all()
    
    # Obtenemos los sucesores del item recibido
    sucesores = Item.query.filter_by(antecesor_id=item.id)
    sucesores = sucesores.all()
     
    # Por cada uno de los hijos
    for hijo in hijos:
        # Si el hijo no se encuentra ya en la lista de items afectados
        if hijo != None and (not(hijo in lista_items)):
            # Se agrega a la lista
            lista_items.append(hijo)
            # Se llama recursivamente al metodo, pasando la lista actualizada y el hijo como
            # item a analizar
            calculo_adelante(hijo, lista_items)
        
    # Por cada uno de los sucesores
    for sucesor in sucesores:
        # Si el sucesor no se encuentra ya en la lista de items afectados
        if sucesor != None and (not(sucesor in lista_items)):
            # Se agrega a la lista
            lista_items.append(sucesor)
            # Se llama recursivamente al metodo, pasando la lista actualizada y el sucesor como
            # item a analizar
            calculo_adelante(sucesor, lista_items)
    
    # Retornamos la lista de items que seran afectados hacia adelante
    return lista_items
    
    
def calculo_atras(item, lista_items): 
    """
    Metodo que obtiene todos los items que seran afectado hacia atras por el impacto de un item
    @param item: item que sera analizado hacia atras
    @param lista_items: lista de los items afectados, la cual sera actualizada
    @return: lista de items afectadoa hacia atras
    """
    
    # Obtenemos el padre del item a analizar
    padre = Item.query.filter_by(id=item.padre_id).first()
    
    # Si el padre no se encuentra ya en la lista de items afectados
    if padre!= None and (not(padre in lista_items)):
        # Lo agregamos a la lista
        lista_items.append(padre)
        # Se llama recursivamente al metodo, pasando la lista actualizada y el padre como
        # item a analizar
        calculo_atras(padre, lista_items)
    
    # Obtenemos el antecesor del item a analizar
    antecesor = Item.query.filter_by(id=item.antecesor_id).first()
    
    # Si el anteccesor no se encuentra ya en la lista de items afectados
    if antecesor != None and (not(antecesor in lista_items)):
        # Lo agregamos a la lista
        lista_items.append(antecesor)
        # Se llama recursivamente al metodo, pasando la lista actualizada y el padre como
        # item a analizar
        calculo_atras(antecesor, lista_items)
    
    # Retornamos la lista de items que seran afectados hacia atras
    return lista_items


def calculo_costo(lista_items):
    """
    Metodo que calcula el costo total de una lista de items
    @param lista_items: lista de items cuyo costo total se desea calcular
    @return: costo total de ls lista de items 
    """
    
    # Inicializamos el costo a cero
    costo = 0
    
    # Por cada item de la lista de items
    for item in lista_items:
        # Sumamos su costo al costo total
        costo = costo + item.costo
    
    # Retornamos el costo total    
    return costo


def verificar_autorizacion_fase(usuario = None, fase = None, permisoRequerido = None):
    """
    Verifica que el usuario posea pemisos para realizar una operacion determinada sobre una
    fase
    @param usuario: usuario que se logueo
    @param fase: fase sobre la cual se desea realizar una operacion
    @param permisoRequerido: permiso que debe poseer el usuario sobre la fase para que pueda
    realizar la acccion
    """
    # Establecemos por defecto que el usuario logueado no esta autorizado para realizar la  operacion
    autorizado = False
    
    #============================================================================
    #  rolAdministrador = db.session.query(Rol).filter_by(nombre = 'Administrador de Sistema').first()
    # 
    #  if rolAdministrador in usuario.roles:
    #          autorizado = True
    #          
    #  else:         
    #============================================================================
    
    # Obtenemos el permisos con los elementos requeridos
    permiso = db.session.query(Permiso).filter_by(nombre = permisoRequerido, entidad_id = fase.entidad_id).first()
    # flash(permiso)
    
    # Si existe dicho permiso
    if(permiso != None):
        # Por cada rol que posea el usuario logueado
        for rol in usuario.roles:
            # Si tiene ese permiso
            if permiso in rol.permiso:
                # Establecemos que esta autorizado para realizar la operacion
                autorizado = True
    
    return autorizado
    